//
//  Log.cpp
//  sad
//
//  Created by Christopher Kemsley on 2/5/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "Log.hpp"
#include <iostream>
#include <fstream>

#include "System.hpp"

#include "Settings.hpp"
#include "Codec.hpp"
#include "Log.hpp"





NamespaceBegin

struct	LogSettings : public Settings
{
	bool	showsErrors ;
	bool	showsWarnings ;
	bool	showsNotices ;
	bool	showsStates ;
	
	std::vector<std::string>	manualSources ;
	
	URL		logPath ;
	URL		stdPath ;
	
	std::ostream*	logStream ;
	bool			ownsLogStream ;
	std::ostream*	stdStream ; // stdout
	bool			ownsStdStream ;
	
	LogSettings ()
	:	Settings ("Log")
	,	showsErrors		( true )
	,	showsWarnings	( false )
	,	showsNotices	( false )
	,	showsStates		( false )
	,	logPath			()
	,	stdPath			()
	,	logStream		( & std::cerr )
	,	ownsLogStream	( false )
	,	stdStream		( & std::cout )
	,	ownsStdStream	( false )
		{
		add ( "showErrors" , showsErrors ) ;
		add ( "showWarnings" , showsWarnings ) ;
		add ( "showNotices" , showsNotices ) ;
		add ( "showStates" , showsStates ) ;
		add ( "manualSources" , manualSources ) ;
		add ( "logPath" , logPath ) ;
		add ( "outPath" , stdPath ) ;
		}
	
	
	
	void	_updateStdErr ()
		{
		if ( ownsLogStream )
			delete logStream ;
		if ( logPath.path().size()==0 )
			{
			logStream = & std::cerr ;
			}
		else
			{
			std::ofstream * stream = new std::ofstream ( logPath.absolutePath().c_str() , std::ios_base::out ) ;
			if ( stream -> is_open() )
				{
				logStream = stream ;
				ownsLogStream = true ;
				}
			else
				{
				logStream = & std::cerr ;
				std::cerr << "Warning: failed to open log file\n" ;
				delete stream ;
				}
			}
		}
	void	_updateStdOut ()
		{
		if ( ownsStdStream )
			delete stdStream ;
		if ( stdPath == logPath )
			{
			stdStream = logStream ;
			ownsStdStream = false ;
			}
		else if ( stdPath.path().size()==0 )
			{
			stdStream = & std::cout ;
			}
		else
			{
			std::ofstream * stream = new std::ofstream ( stdPath.absolutePath().c_str() , std::ios_base::out ) ;
			if ( stream -> is_open() )
				{
				stdStream = stream ;
				ownsStdStream = true ;
				}
			else
				{
				stdStream = & std::cout ;
				std::cerr << "Warning: failed to open output file\n" ;
				delete stream ;
				}
			}
		}
	
	
	
	
	virtual void		validate ()
		{
		_updateStdErr () ;
		_updateStdOut () ;
		(*logStream) << "LogSettings\n" ;
		(*logStream) << "	Errors " << ( showsErrors ? "ARE" : "are NOT" ) << " shown\n" ;
		(*logStream) << "	Warnings " << ( showsWarnings ? "ARE" : "are NOT" ) << " shown\n" ;
		(*logStream) << "	Notices " << ( showsNotices ? "ARE" : "are NOT" ) << " shown\n" ;
		(*logStream) << "	States " << ( showsStates ? "ARE" : "are NOT" ) << " shown\n" ;
		(*logStream) << "	Error output: " << ( logPath.path().size()==0 ? "stderr" : logPath.absolutePath() ) << "\n" ;
		(*logStream) << "	Standard Output: " << ( stdPath.path().size()==0 ? "stdout" : stdPath.absolutePath() ) << "\n\n" ;
		}
	
	
	virtual bool	_doesAlwaysPrint ( std::string const & source ) const
		{
		std::vector<std::string>::const_iterator	here	=	manualSources.begin() ;
		std::vector<std::string>::const_iterator	end		=	manualSources.end() ;
		while ( here < end )
			if (  (*(here++))  ==  source  )
				return true ;
		return false ;
		}
} ;

NamespaceEnd





using namespace Sim ;




void	Log ::	loadConfiguration	( URL & path )
	{
	Codec :: decode<URL,Settings> ( path , settings() ) ;
	}
LogSettings&	Log :: settings()
	{
	static LogSettings s ;
	return s ;
	}



void Log ::	setShowsErrors		( bool value )
	{
	settings().showsErrors = value ;
	}
void Log ::	setShowsWarnings	( bool value )
	{
	settings().showsWarnings = value ;
	}
void Log ::	setShowsNotices		( bool value )
	{
	settings().showsNotices = value ;
	}
void Log ::	setShowsStates		( bool value )
	{
	settings().showsStates = value ;
	}

Log&	Log ::	error		( char const* domain )
	{
	if ( settings().showsErrors || settings()._doesAlwaysPrint(domain) )
		Log::shared() . mCurrent = settings().logStream ;
	else
		Log::shared() . mCurrent = 0 ;
	Interval i = System::shared().currentTime() ;
	return Log::shared() << "\n" << i << "  Error:  (" << domain << ")   " ;
	}
Log&	Log ::	warning		( char const* domain )
	{
	if ( settings().showsWarnings || settings()._doesAlwaysPrint(domain) )
		Log::shared() . mCurrent = settings().logStream ;
	else
		Log::shared() . mCurrent = 0 ;
	Interval i = System::shared().currentTime() ;
	return Log::shared() << "\n" << i << "  Warning:  (" << domain << ")   " ;
	}
Log&	Log ::	notice		( char const* domain )
	{
	if ( settings().showsNotices || settings()._doesAlwaysPrint(domain) )
		Log::shared() . mCurrent = settings().logStream ;
	else
		Log::shared() . mCurrent = 0 ;
	Interval i = System::shared().currentTime() ;
	return Log::shared() << "\n" << i << "  Notice:  (" << domain << ")   " ;
	}
Log&	Log ::	states		( char const* domain )
	{
	if ( settings().showsStates || settings()._doesAlwaysPrint(domain) )
		Log::shared() . mCurrent = settings().logStream ;
	else
		Log::shared() . mCurrent = 0 ;
	Interval i = System::shared().currentTime() ;
	return Log::shared() << "\n" << i << "  State:  (" << domain << ")   " ;
	}
Log&	Log ::	output		()
	{
	Log::shared() . mCurrent = settings().stdStream ;
	return Log::shared() ;
	}

void	Log :: flush		()
	{
	(*(settings().logStream)) << std::flush ;
	(*(settings().stdStream)) << std::flush ;
	}

void	Log :: logException	( Exception const& exception )
	{
	(*(settings().logStream)) << "\n\n" << exception << "\n\n" ;
	}


Log :: Log ()
:	mCurrent ( 0 )
	{
	
	}
Log&	Log :: shared ()
	{
	static Log l ;
	return l ;
	}
